# In pr038 we have seen how to use a ListModel. In this example we will modify the last example to make a TableModel.
# In this example we will see how a model and views communicate with each other in Qt and also, how a model stores a data.
# So we will create our own model class and understand how a model interacts with a view.
# There are three ways in which the data can be represented 1) List based (1-Dim), 2) Table based (2-Dim) and 3) Tree based (Hierarchical).
# In this example we will work with List based model.
# A model is just an interface that the view communicates with to retrieve some data to display. The model is just a wrapper for the data.
# The view doesn't know how to get the data. All it can do is pass some values to the model and hope for the best. Its the model's job to
# take the coming values from the view and translate them.

import sys
from PySide import QtGui, QtCore


# To create a model, we have to subclass one of the abstract models. We will be subclassing QAbstractTable model. All the models starting
# with abstract have to be subclasses. You can't use them directly.
class PalletteTableModel(QtCore.QAbstractTableModel):
    # The model will receive a list of colors and the parent.
    # Note that the colors list is now a nested list.
    def __init__(self, colors = [[]], headers = [], parent = None):
        # First we need to call the super-class constructor and pass the parent argument to it
        QtCore.QAbstractTableModel.__init__(self, parent)
        # We are making a model that will store colors, so we need a private list that would take the incoming color list.
        self.__colors = colors
        self.__headers =  headers
        
    # Every model in Qt must implement two methods at minimum, i.e. 1) row count and 2) data
    # The row count tell the view how many item this model contains. For a list based model, we can ignore the parent but for hirarchical based
    # view, the parent will be important.
    # For TableModel, we have to implement both the rowCount as well as the columnCount methods.
    def rowCount(self, parent):
        return len(self.__colors)
    
    def columnCount(self, parent):
        return len(self.__colors[0]) # The total number of zeroth elements give the total columns.
    
    # The data method is responsible to provide the data to be displayed by the view based on its index value
    def data(self, index, role):

        # For displaying tooltip
        if role == QtCore.Qt.ToolTipRole:
            row = index.row()
            column = index.column()
            # Use both the row and column to index the multi-dimensional list
            return "Hex code is: " + self.__colors[row][column].name()
        
        # For displaying the color icon
        if role == QtCore.Qt.DecorationRole:
            row = index.row()
            column = index.column()
            value = self.__colors[row][column]
            # First create a pixmap
            pixmap = QtGui.QPixmap(30, 30)
            pixmap.fill(value)
            # Then create an icon with that pixmap
            icon = QtGui.QIcon(pixmap)
            return icon
            
        # For displaying value
        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            column = index.column()
            value = self.__colors[row][column]
            return value.name()
        
        # For keeping the old data there when double clicked to edit
        if role == QtCore.Qt.EditRole:
            row = index.row()
            column = index.column() 
            return self.__colors[row][column].name()
    
    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            
            if orientation == QtCore.Qt.Horizontal:
                if section < len(self.__headers):
                    return self.__headers[section]
                else:
                    return "Temp"
            else:
                return "Color %s" % section
    
    # To make the items editable
    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
    
    def setData(self, index, value, role = QtCore.Qt.EditRole):
        # Index -> stores information where the item is.
        # value -> The edited value (it is always a string). We take that string, convert it to a caller?? and assign it to our
        # private list in the edited row. If the operation is successful, returns true.
        if role == QtCore.Qt.EditRole: # It is also the default role as we set it already
            row = index.row()
            column = index.column()
            color = QtGui.QColor(value)
            if color.isValid():
                self.__colors[row][column] =  color
                self.dataChanged.emit(index, index)
                return True # if color is valid then return true else return false
            return False
        
    # Inserting rows into the model
    def insertRows(self, position, rows, parent = QtCore.QModelIndex()):
        # position -> where in the data structure we want to insert
        # rows -> how many rows we want to insert
        # parent -> The parent is defaulted to an empty QModelIndex
        self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
        # self.beginInsertRows(index, first, last)
        # First and Last are the indices of where the views should update. All items between the first
        # and last including those two are the items we are inserting and the views must know those indices so they can display the correct data.
        # The index parameter is only used when we are working with hierarchical data structures. Here we want to default to root. 
        for i in range(rows):
            # Because we need to insert a row with columnCount amount of items with black as default color. The below line will create that for us. 
            defaultValues = [QtGui.QColor("#000000") for i in range(self.columnCount(None))]
            self.__colors.insert(position, defaultValues) # Default values is now a list as required by the insert method. 
        self.endInsertRows()
        return True
    
    # Removing rows from the model
    def removeRows(self, position, rows, parent = QtCore.QModelIndex()):
        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)

        for i in range(rows):
            value = self.__colors[position]
            self.__colors.remove(value)
            
        self.endRemoveRows()
        return True
    
    
    # Inserting columns into the model
    def insertColumns(self, position, columns, parent = QtCore.QModelIndex()):
        self.beginInsertColumns(QtCore.QModelIndex(), position, position + columns - 1)
        
        rowCount = len(self.__colors)
        for i in range(columns):
            for j in range(rowCount):
                self.__colors[j].insert(position, QtGui.QColor("#000000")) 
        self.endInsertColumns()
        return True
    
    # Removing columns from the model
    def removeColumns(self, position, columns, parent = QtCore.QModelIndex()):
        self.beginRemoveColumns(QtCore.QModelIndex(), position, position + columns - 1)
        
        rowCount = len(self.__colors)
        print(rowCount)
        for i in range(columns):
            for j in range(rowCount):
                value = self.__colors[j][position]
                self.__colors[j].remove(value)
        print(value)

        self.endRemoveColumns()
        return True
    


if __name__ == '__main__':
    appObject = QtGui.QApplication(sys.argv)
    appObject.setStyle("plastique")
    
    # All of our views
    listview = QtGui.QListView()
    listview.show()
    
    # Tree view cannot be used with table-view model. It crashes the application.
    
    combobox = QtGui.QComboBox()
    combobox.show()
     
    tableview = QtGui.QTableView()
    tableview.show()
    
    red = QtGui.QColor(255, 0, 0)
    green = QtGui.QColor(0, 255, 0)
    blue = QtGui.QColor(0, 0, 255)
    
    
#     tableData = [
#                  ["0" ,  "1" , "2"  , "3"],
#                  ["4" ,  "5" , "6"  , "7"],
#                  ["8" ,  "9" , "10" , "11"],
#                  ["12", "13" , "14" , "15"]
#                 ]
    
    rowCount = 4
    columnCount = 6
    headers = ["Pallette0", "Colors", "Brushes", "Hey", "There", "Baby"]
    
    # Note that this is a specialized for loop. What is does is, for each rowCount, it will return a list. And that list, will return
    # a color for each columnCount.
    tableData = [[QtGui.QColor("#FFFF00") for i in range(columnCount)] for j in range(rowCount)]
    # This will give us the following multi-dimensional list with a single line of code.
#     tableData = [
#                  [QtGui.QColor("#FFFF00") ,  QtGui.QColor("#FFFF00") , QtGui.QColor("#FFFF00")  , QtGui.QColor("#FFFF00")],
#                  [QtGui.QColor("#FFFF00") ,  QtGui.QColor("#FFFF00") , QtGui.QColor("#FFFF00")  , QtGui.QColor("#FFFF00")],
#                  [QtGui.QColor("#FFFF00") ,  QtGui.QColor("#FFFF00") , QtGui.QColor("#FFFF00")  , QtGui.QColor("#FFFF00")],
#                  [QtGui.QColor("#FFFF00") ,  QtGui.QColor("#FFFF00") , QtGui.QColor("#FFFF00")  , QtGui.QColor("#FFFF00")]
#                 ]
    
    
    # Create the model from the PalletteTabletModel with tableData and column headers passed as arguments
    model = PalletteTableModel(tableData, headers)

    # Assign the model to views
    listview.setModel(model)
    # Tree view cannot be used with table-view model. It crashes the application.
    combobox.setModel(model)
    tableview.setModel(model)
    
#     # Insert some rows at index 2
#     model.insertRows(2, 5)
#     
#     # Remove some rows at index 2
#     model.removeRows(2, 2)
#     
    # Insert some columns at index 1
    model.insertColumns(0, 2)
     
    # Remove some columns at index 1
    model.removeColumns(0, 2)
    
    sys.exit(appObject.exec_())
    